#!/usr/bin/env python
# -*- coding: utf-8 -*-
import binascii
import Crc16


class Analysis(object):

    """
    天然气远程抄表系统通用解析类
    """

    def __init__(self, data):
        # 接收到传来的数据转换成二进制数据然后在用十六进制表示
        # 先判断是否有转义的字符，有转义的字符要单独处理
        print "source_data_old", binascii.b2a_hex(data)
        data = bytearray(data)
        replace_index = []
        for (i, d) in enumerate(data):
            # 判断如果出现了0x5c，先移除0x5c,再将其下一位减去0x80,得到真实的转义之前的字符
            if d == 0x5c:
                replace_index.append(i)
                data.remove(0x5c)
        for i in replace_index:
            data[i] -= 0x80
        self.source_data = binascii.b2a_hex(data)
        print "source_data:", self.source_data
        # 将接收到数据每两个字符增加一个空格
        data_new = ""
        for (i, d) in enumerate(self.source_data):
            if i % 2 == 1:
                data_new += "%s " % d
            else:
                data_new += "%s" % d
        # 用于校验的列表,并将十六进制转换为十进制
        self.data = map(lambda x: int(x, 16), data_new.strip().split(' ')[:-1])
        print "data:", self.data

    def _get_data_length(self):
        """
        获取数据长度
        :return: 返回数据长度，类型int
        """
        return int(self._int2asciistr(self.data[7], 2) + self._int2asciistr(self.data[8], 2), 16)

    def _get_data(self):
        """
        获取数据
        :return:返回数据，类型为str的列表
        """
        # 得到数据长度
        data_length = self._get_data_length()
        return map(str, self.data[9:9 + data_length])

    def _ascii2intstr(self, data):
        """
        将ascii码列表转换为十进制数据字符串
        :param data: ascii码数字列表
        :return: 十进制数据字符串
        """
        # 根据ascii码转换为对应的字符
        data = map(hex, map(int, data))
        for (i, d) in enumerate(data):
            if len(d)==3:
                data[i] = d[:2] + '0' + d[2:]
        # 替换字符中的\x并拼接为字符串
        data = ''.join(map(lambda x: x.encode('string-escape').replace(r'0x', ''), data))
        return str(int(data, 16))

    def _int2asciistr(self, data, data_len):
        """
        将十进制转换为ascii码字符串
        :param data:要转换的数字
        :return:转换后的ascii码
        """
        # 根据ascii码转换为对应的字符
        data = hex(int(data)).encode('string-escape').replace(r'0x', '')
        data = (data_len-len(data))*'0'+data
        result_str = ''
        for (i, d) in enumerate(data):
            if i % 2 == 0:
                result_str += ' '
            result_str += d
        return result_str.strip()

    def _generate_session(self):
        """
        获取session id
        :return: 返回session id，长度4位
        """
        pass

    def check_crc(self):
        """
        crc16校验
        :return:校验状态，True校验成功，False校验失败
        """
        if Crc16.Crc16.calcrc(self.data):
            return True
        print "Crc16校验失败"
        return False

    def get_sys_type(self):
        """
        获取系统类型，长度1位
        1 天然气
        :return: 系统类型编码，类型str
        """
        return str(self.data[1:2][0])

    def get_order_type(self):
        """
        获取指令类型，长度1位
        1 注册
        2 ACK
        3 数据
        4 心跳
        :return:指令类型编码，类型str
        """
        return str(self.data[2:3][0])

    def get_id(self):
        """
        获取ID或SESSIONID（注册时表明集中器ID，其它时候为sessionID）
        :return:返回id号，类型str
        """
        return self._ascii2intstr(self.data[3:7])

    def get_concentrator_passwod(self):
        """
        获取集中器密码,只有指令为注册类型才可调用
        :return:返回集中器密码，长度4位，类型str
        """
        # 判断是否为注册的类型
        return self._ascii2intstr(self._get_data()[:4])

    def get_phone(self):
        """
        获取当前本机手机号
        :return:返回手机号，长度11位，类型为str
        """
        # 判断是否为注册的类型
        if self.get_order_type() == '1':
            phone = ""
            phone_array = [self._int2asciistr(n, 2) for n in self._get_data()[4:15]]
            for num in phone_array:
                phone += str(int(num, 16)-int('30', 16))
            return phone
        else:
            print '不是注册类型，不能获取手机号码'

    def get_control_field_tag(self):
        """
        获取控制字段信息（控制标志）
        :return:控制字段信息
        """
        return self._ascii2intstr(self._get_data()[4:5])

    def get_control_field_pack(self):
        """
        获取控制字段信息（数据包信息）
        :return:控制字段信息
        """
        return self._ascii2intstr(self._get_data()[5:6])

    def get_curdata_num(self):
        """
        当前数据包帧号
        :return: 数据库包帧号
        """
        return self._ascii2intstr(self._get_data()[6:7])

    def get_flowmeter_num(self):
        """
        流量计数量
        :return: 流量计数量
        """
        return self._ascii2intstr(self._get_data()[7:8])

    # def get_flowmeter_id(self):
    #     """
    #     流量计ID
    #     :return: 流量计ID
    #     """
    #     return self._ascii2intstr(self._get_data()[8:9])
    #
    # def get_flowmeter_type(self):
    #     """
    #     获取流量计协议类型
    #     :return:返回流量计协议类型
    #     """
    #     return self._ascii2intstr(self._get_data()[16:17])

    def _get_flowmeter_modbus(self, flowmeter_num):
        """
        流量计信息计算公共方法（modbus协议）
        :return:
        """
        if len(flowmeter_num) == 8:
            # 前6位是整数，后2位是小数
            num_int = int(self._ascii2intstr(flowmeter_num[:6]))
            num_float = float(self._ascii2intstr(flowmeter_num[6:8])) / 2 ** 16
        else:
            # 前3位是整数，后1位是小数
            num_int = int(self._ascii2intstr(flowmeter_num[:3]))
            num_float = float(self._ascii2intstr(flowmeter_num[3:4])) / 2 ** 8
        return format(num_int + num_float, '.3f')

    def _get_flowmeter_rs485(self, flowmeter_num):
        """
        流量计信息计算公共方法（rs485协议）
        :return:
        """
        num_head = int(self._ascii2intstr(flowmeter_num[:1]))
        num_int = 2 ** num_head
        if len(flowmeter_num) == 8:
            # 前1位是head，后5位是浮点数小数位，后2位直接舍弃
            # 2的head次方+后5位浮点数小数位，保留3位小数
            num_float = float(self._ascii2intstr(flowmeter_num[1:6]))
            while num_float > 1:
                num_float /= 2
        else:
            # 前1位是head，后3位是浮点数小数位
            # 2的head次方+后3位浮点数小数位，保留3位小数
            num_float = float(self._ascii2intstr(flowmeter_num[1:4]))
            while num_float > 1:
                num_float /= 2
        return format(num_int * num_float, '.3f')

    def get_flowmeter_data(self):
        """
        流量计数据
        :return:
        """
        # 流量计数量之后的数据
        data = self._get_data()[8:]
        # 流量计总数
        flowmeter_num = int(self.get_flowmeter_num())
        tag = len(data) / flowmeter_num
        flowmeters = []
        for i in xrange(flowmeter_num):
            flowmeter = dict()
            # 流量计ID 1
            flowmeter['flowmeter_id'] = self._ascii2intstr(data[i * tag:i * tag + 1])
            # 流量计数据包长度 1
            flowmeter['data_length'] = self._ascii2intstr(data[i * tag + 1:i * tag + 2])
            # 时间信息 6
            flowmeter['time'] = self._ascii2intstr(data[i * tag + 2:i * tag + 8])
            # 协议类型 1
            flowmeter['flowmeter_type'] = self._ascii2intstr(data[i * tag + 8:i * tag + 9])
            if flowmeter['flowmeter_type'] == '1':
                # 工况累计量
                flowmeter['total_g'] = self._get_flowmeter_modbus(data[i * tag + 9:i * tag + 17])
                # 标况累计量
                flowmeter['total_b'] = self._get_flowmeter_modbus(data[i * tag + 17:i * tag + 25])
                # 工况流量
                flowmeter['g'] = self._get_flowmeter_modbus(data[i * tag + 25:i * tag + 29])
                # 标况流量
                flowmeter['b'] = self._get_flowmeter_modbus(data[i * tag + 29:i * tag + 33])
                # 温度
                temperature = data[i * tag + 33:i * tag + 37]
                # 温度大于等于128则是负数
                if int(temperature[0]) >= 128:
                    # 负数的话让第一位的十六进制值和\x7f与运算，并将得到的值拿去计算温度，并在计算出的温度加负号
                    temperature[0] = int(temperature[0]) & 0x7f
                    flowmeter['temperature'] = '-'+self._get_flowmeter_modbus(temperature)
                else:
                    flowmeter['temperature'] = self._get_flowmeter_modbus(temperature)
                # 压力
                flowmeter['pressure'] = self._get_flowmeter_modbus(data[i * tag + 37:i * tag + 41])
                # 状态
                flowmeter['status'] = self._ascii2intstr(data[i * tag + 41:i * tag + 42])
            elif flowmeter['flowmeter_type'] == '2' or flowmeter['flowmeter_type'] == '4':
                # 工况累计量
                flowmeter['total_g'] = self._get_flowmeter_rs485(data[i * tag + 9:i * tag + 17])
                # 标况累计量
                flowmeter['total_b'] = self._get_flowmeter_rs485(data[i * tag + 17:i * tag + 25])
                # 工况流量
                flowmeter['g'] = self._get_flowmeter_rs485(data[i * tag + 25:i * tag + 29])
                # 标况流量
                flowmeter['b'] = self._get_flowmeter_rs485(data[i * tag + 29:i * tag + 33])
                # 温度
                temperature = data[i * tag + 33:i * tag + 37]
                # 温度大于等于128则是负数
                if int(temperature[1]) >= 128:
                    # 负数的话让第二位的十六进制值和\x7f与运算，并将得到的值拿去计算温度，并在计算出的温度加负号
                    temperature[1] = int(temperature[1]) & 0x7f
                    flowmeter['temperature'] = '-' + self._get_flowmeter_rs485(temperature)
                else:
                    flowmeter['temperature'] = self._get_flowmeter_rs485(temperature)
                # 压力
                flowmeter['pressure'] = self._get_flowmeter_rs485(data[i * tag + 37:i * tag + 41])
                # 状态
                flowmeter['status'] = self._ascii2intstr(data[i * tag + 41:i * tag + 42])
            flowmeters.append(flowmeter)
        return flowmeters

    # def get_flowmeter_total_g(self):
    #     '''
    #     流量计信息【工况累积量】
    #     :return:
    #     '''
    #     return self._get_flowmeter_modbus(self._get_data()[17:25])
    #
    # def get_flowmeter_total_b(self):
    #     '''
    #     流量计信息【标况累积量】
    #     :return:
    #     '''
    #     return self._get_flowmeter_modbus(self._get_data()[25:33])
    #
    # def get_flowmeter_g(self):
    #     '''
    #     流量计信息【工况流量】
    #     :return:
    #     '''
    #     return self._get_flowmeter_modbus(self._get_data()[33:37])
    #
    # def get_flowmeter_b(self):
    #     '''
    #     流量计信息【标况流量】
    #     :return:
    #     '''
    #     return self._get_flowmeter_modbus(self._get_data()[37:41])
    #
    # def get_flowmeter_temperature(self):
    #     '''
    #     流量计信息【温度】
    #     :return:
    #     '''
    #     return self._get_flowmeter_modbus(self._get_data()[41:45])
    #
    # def get_flowmeter_pressure(self):
    #     '''
    #     流量计信息【压力】
    #     :return:
    #     '''
    #     return self._get_flowmeter_modbus(self._get_data()[45:49])
    #
    # def get_flowmeter_status(self):
    #     '''
    #     流量计信息【状态】
    #     :return:
    #     '''
    #     return self._ascii2intstr(self._get_data()[49:50])

    def get_ack(self, type):
        '''
        该指令由服务器发给集中器，是服务器对集中器的请求指令及对集中器数据传送的回应指令。指令类型号为0x04.
        该方法得到应该返回的指令码
        :return:返回ack指令
        '''
        # 使用集中器ID作为sessionid
        # sessionid，集中器id
        concentrator_id = self._int2asciistr(self.get_id(), 8)
        # 数据[集中器密码]
        concentrator_passwod = self._int2asciistr(self.get_concentrator_passwod(), 8)
        # 数据[集中器上一请求的指令类型]
        request_type = self._int2asciistr(self.get_order_type(), 2)
        # 控制标志
        # 0x08:服务器通知集中器注册成功（集中器密码+集中器上一请求的指令类型（0x01）+控制标志（0x08）+新分配的端口号）
        # 0x06：服务器通知集中器，对集中器发来的数据包接收完成
        control_tag = '08' if type == 'reg' else '06'
        # 包信息(端口)
        package_info = '27 0e'
        # 计算数据长度
        data_length = len(concentrator_passwod.replace(' ', '')) / 2 + \
                      len(request_type.replace(' ', '')) / 2 + \
                      len(control_tag.replace(' ', '')) / 2 + \
                      len(package_info.replace(' ', '')) / 2
        data_length = self._int2asciistr(data_length, 4)
        # 开始符 系统类型 指令类型 ...
        ack_str = '24 01 04 '\
                  +concentrator_id+' '\
                  +data_length+' '\
                  +concentrator_passwod+' '\
                  +request_type+' '\
                  +control_tag+' '\
                  +package_info
        # crc16校验码
        ack_list = map(lambda x: int(x, 16), ack_str.split(' '))
        wcrc = Crc16.Crc16.createcrc(ack_list)
        # 拼接校验码和结束符
        ack_str += ' '+self._int2asciistr(str(wcrc), 4)+' 55'
        ack_list = [int('0x'+ack,16) for ack in ack_str.split(' ')]
        return bytearray(ack_list)